/* ---------------------------------------------------------------------
 *
 * -- Privacy-preserving Sharing of Sensitive information Toolkit (PSST)
 *    (C) Copyright 2010 All Rights Reserved
 *
 * -- PSST routines -- Version 1.0.1 -- April, 2010
 * FNP corresponds to protocol FNP04 in the paper
 *
 * Author         : Yanbin Lu
 * Security and Privacy Research Outfit (SPROUT),
 * University of California, Irvine
 *
 * -- Bug report: send email to yanbinl@uci.edu
 * ---------------------------------------------------------------------
 *
 * -- Copyright notice and Licensing terms:
 *
 *  Redistribution  and  use in  source and binary forms, with or without
 *  modification, are  permitted provided  that the following  conditions
 *  are met:
 *
 * 1. Redistributions  of  source  code  must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce  the above copyright
 *    notice,  this list of conditions, and the  following disclaimer in
 *    the documentation and/or other materials provided with the distri-
 *    bution.
 * 3. The name of the University,  the SPROUT group,  or the names of its
 *    authors  may not be used to endorse or promote products deri-
 *    ved from this software without specific written permission.
 *
 * -- Disclaimer:
 *
 * THIS  SOFTWARE  IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,  INCLUDING,  BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,  INDIRECT, INCIDENTAL, SPE-
 * CIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 * TO,  PROCUREMENT  OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEO-
 * RY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  (IN-
 * CLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#include "fnp_psi_server.h"
#include "memreuse.h"
#include "mpz_tool.h"
#include "crypto_tool.h"
#include "basic_tool.h"
#include <stdio.h>
#include <math.h>

FNP_PSI_Server::FNP_PSI_Server()
{
  m_iNumIterations = 1;
  m_g = Memreuse::New();
  m_h = Memreuse::New();
  m_p = Memreuse::New();
  m_q = Memreuse::New();
  m_k = Memreuse::New();
}


void FNP_PSI_Server::LoadData()
{
  m_set.clear();
  m_rawset.clear();
  ifstream infile(m_strInputFile.c_str());
  if (!infile)
	{
	  cerr<<"open "<<m_strInputFile<<" error"<<endl;
	  return;
	}
  string element;
  while (getline(infile, element) && infile.good())
	{
	  m_rawset.push_back(element);
	  mpz_t * e = Memreuse::New();
	  CryptoTool::FDH(e, element, m_p);
	  m_set.push_back(e);
	}
}

void FNP_PSI_Server::Setup(int nbits)
{
  stringstream keyfile;
  keyfile<<"dsa."<<nbits<<".pub";
  BasicTool::Load_ElGamal_Pub_Key(m_g, m_p, m_q, m_h, keyfile.str());
  LoadData();

  rn.Initialize(nbits);
}

void FNP_PSI_Server::Initialize(int nbits)
{
  mpz_t * y = Memreuse::New();
  mpz_t * r = Memreuse::New();
  for (size_t i = 0; i < m_set.size(); i++)
    {
      // encrypt m_set[i]
      mpz_t * yl = Memreuse::New();
      mpz_t * yr = Memreuse::New();
	  mpz_powm(*y, *m_g, *m_set[i], *m_p);
	  CryptoTool::ElGamalEnc(yl, yr, y, m_g, m_p, m_q, m_h);
      m_vecY_L.push_back(yl);
      m_vecY_R.push_back(yr);
    }
  Memreuse::Delete(r);
  Memreuse::Delete(y);
}

void FNP_PSI_Server::Evaluate(mpz_t * result_l, mpz_t * result_r, mpz_t * y)
{
  mpz_set(*result_l, *m_vecElGamal_L[m_vecElGamal_L.size()-1]);
  for (size_t i = m_vecElGamal_L.size()-1; i >= 1; i --)
	{
	  mpz_powm(*result_l, *result_l, *y, *m_p);
	  mpz_mul(*result_l, *result_l, *(m_vecElGamal_L[i-1]));
	  mpz_mod(*result_l, *result_l, *m_p);
	}
  mpz_set(*result_r, *m_vecElGamal_R[m_vecElGamal_R.size()-1]);
  for (size_t i = m_vecElGamal_R.size()-1; i >= 1; i --)
	{
	  mpz_powm(*result_r, *result_r, *y, *m_p);
	  mpz_mul(*result_r, *result_r, *(m_vecElGamal_R[i-1]));
	  mpz_mod(*result_r, *result_r, *m_p);
	}
}

size_t FNP_PSI_Server::WhichBin(mpz_t * id, mpz_t * k)
{
  mpz_t * newid = Memreuse::New();
  mpz_xor (*newid, *id, *k);
  mpz_mod_ui (*newid, *newid, m_B);
  unsigned long key = mpz_get_ui (*newid);
  Memreuse::Delete(newid);
  return key;
}


int FNP_PSI_Server::OnRequest1(const vector<string>& input)
{

  mpz_t * r = Memreuse::New();
  stringstream ss;
  ss.str(input[0]);
  ss>>m_M;
  ss.clear();
  ss.str(input[1]);
  ss>>m_B;
  m_vecPoly.resize(m_B);
  MpzTool::Mpz_from_string(m_k, input[2]);
  for (size_t i = 0; i < m_set.size(); i++)
	{
	  size_t b = WhichBin(m_set[i], m_k);
	  m_vecPoly[b].push_back(i);
	}

  mpz_t * result_l = Memreuse::New();
  mpz_t * result_r = Memreuse::New();
  mpz_t * yl, * yr;
  int i = 3;
  // evaluate each bin
  for (size_t b = 0; b < m_B; b++)
	{
	  m_vecElGamal_L.clear();	// clear coefficient for previous bin
	  m_vecElGamal_R.clear();			// forget to release memory for el, er
	  for (size_t m = 0; m <= m_M; m++) // M+1 coefficients
		{
		  mpz_t * el = Memreuse::New();
		  mpz_t * er = Memreuse::New();
		  MpzTool::Mpz_from_string(el, input[i++]);
		  MpzTool::Mpz_from_string(er, input[i++]);
		  m_vecElGamal_L.push_back(el);
		  m_vecElGamal_R.push_back(er);
		}
	  for (size_t i = 0; i < m_vecPoly[b].size(); i++)
		{
		  size_t j = m_vecPoly[b][i];
		  yl = m_vecY_L[j];
		  yr = m_vecY_R[j];
		  // evaluate function
		  Evaluate(result_l, result_r, m_set[j]);
		  rn.Get_Z_Range(r, m_q);
		  // r*P(y)
		  mpz_powm(*result_l, *result_l, *r, *m_p);
		  mpz_powm(*result_r, *result_r, *r, *m_p);
		  // r*P(y)+y
		  mpz_mul(*result_l, *result_l, *yl);
		  mpz_mul(*result_r, *result_r, *yr);
		  mpz_mod(*result_l, *result_l, *m_p);
		  mpz_mod(*result_r, *result_r, *m_p);

		  m_output.push_back(MpzTool::String_from_mpz(NULL, result_l));
		  m_output.push_back(MpzTool::String_from_mpz(NULL, result_r));		  
		}
	}

  Memreuse::Delete(result_l);
  Memreuse::Delete(result_r);
  Memreuse::Delete(r);
  for (size_t i = 0; i < m_set.size(); i++)
	{
	  Memreuse::Delete(m_vecY_L[i]);
	  Memreuse::Delete(m_vecY_R[i]);
	}
  return 0;
}

